/*
 Copyright 2008 Jason E. Smith (www.thoughthill.com) Licensed under the Apache License, Version 2.0 (the "License");
 You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 */
new Class("controls::Widget", {
    inherits  : controls.HtmlControl,
	
	//TODO: refactor
    asset_path: "javascripts/application/controls/${CLASSNAME}/${ASSET}/${FILENAME}",
	
	preInitialize : function(){
		var args = arguments;
		var model = (args[0] || this.model || {});
		this.addEvent("onViewLoaded", function(){
			this.beforeInitialize.apply(this, args);
            (this.initialize) ? this.initialize.apply(this, args) : this;
			if(this.onready) this.onready.call(this, null);
            this.afterInitialize.apply(this, args);
			this.constructor.triggerEvent("onClassLoaded", this.getClassName());
		}.bind(this));
		setTimeout(function(){
			this.bindmodel(model,this.bindview.bind(this));
		}.bind(this),100); 
		return this;
    },
	
	afterInitialize : function(){
		if(this.autorender) this.render();
	},
	
	onready : function(func){
		if(!Object.isElement(this.element) && (func && typeof func == "function")) {
			this.addEvent("onViewLoaded", func.bind(this));
			if(this.element && Object.isElement(this.element)) {
				this.removeEvent("onViewLoaded", func)
				func();
			}
		} else { if(func && Object.isElement(this.element)) func() }
		return true;
	},
	
	addEvent : function(evtname, callback, target){
		return events.QEvent.add((target || this), evtname, callback);
	},
	
	triggerEvent : function(evtname, data, srcTarget){
		events.QEvent.fire((srcTarget||this), evtname, (data||this));
	},
	
	toJSON : function(){ return this.model.toJSON() },
	
	toXML  : function(){ return this.model.toXML()  },
	
	bindmodel : function(args, callback) {
        if (args) {
            for (key in args) {
                if(!(/^_(.)+/.test(key))) {this[key] = args[key];}
            }
		}
		var model = this.model || {};

		if(model && (model instanceof js.Model)) {
			this.model = model;
			callback(this.model);
		}
		else if(model && (typeof model == "object")) {
			this.model = new js.Model(model);
			callback(this.model);
		}
		else if(model && typeof model == "string") {
			//load model/json from external http url (cross domain as well?)
			if(/^(http\:\/\/|\/)(.)+/.test(model)) { //is it an http url?
				console.error("Not yet implemented. The feature to allow Widgets to load their models(.json) over an http url/service but is not yet supported.")
			}
			//load from asset folder
			else if (/^[A-Za-z0-9]*\.[A-Za-z]{1,4}/.test(model)) { //is it just a filename.ext ?
				var asset_type = model.split(".")[1];
					asset_type = (asset_type && /(json|js)/.test(asset_type)) ? "json" : "js";
				var url = this.asset_path.replace(/\$\{CLASSNAME\}/ig, this.getClassName()).replace(/\$\{ASSET\}/ig, asset_type).replace(/\$\{FILENAME\}/,model);
				this.loadasset(url, function(json_object){
					var o = eval("(" + json_object + ")");
					this.model = new js.Model(o);
					callback(this.model);
				}.bind(this));
			}
			else if(/^\{[\".]*\:/.test(model)) {
				var _model = JSON.parse(model);
				this.model = new js.Model(_model);
				callback(this.model);
			}
		}
	},
	
	prepareElement : function(el){
		this.element = el;
		this.setClassName();
		this.bindEvents();
		this.setProto();
		this.triggerEvent("onViewLoaded",this.element);
	},
	
	setProto : function(element){
		element = element || this.element;
		if(element) element.proto = this;
	},
	
	
	bindview : function(model) {
		this.load_css();
		var val   = this.get("element"); 
		var self  = this;
			
		if( val && Object.isElement(val)) {this.prepareElement(val);}
		
		else if(val && typeof val == "string") {
			if(/^\<(.)+\>.*/.test(val)){//todo: check for spaces, tabs, newlines in the beginning of html string
				var evaledHtml = Jst.evaluate_jst(val,this); //TrimPath.processDOMTemplate(val,this);
				var element = evaledHtml.toHtmlElement();
				if(element && Object.isElement(element)) {
					this.prepareElement(element);
				}
				else {
					throw new Error("Error building '" + this.getClassName() + "' view from 'element':\n" + this.get("element"));
				}
			}
			else if(/^(http\:\/\/|\/)(.)+/.test(val)) {
				
			}
			else if (/^[A-Za-z0-9]*\.[A-Za-z]{1,5}/.test(val)) {
				var htmlpath = this.assetpath(val + ".html");
				requires(htmlpath, function onloaded(req){
					var evaledHtml = Jst.evaluate_jst(req.responseText,self); //TrimPath.processDOMTemplate(req.responseText,self);
					self.constructor.prototype.element = req.responseText;
	           		var el = evaledHtml.toHtmlElement().cloneNode(true);
					self.prepareElement(el); //todo: rename prepareElement
				});
			}
		}
		else if(!val){
			var htmlpath = this.assetpath(this.getClassName() + ".html"); 
			requires(htmlpath, function onloaded(req){
				var evaledHtml = Jst.evaluate_jst(req.responseText,self); //TrimPath.processDOMTemplate(req.responseText,self);
				self.constructor.prototype.element = req.responseText;
           		var el = evaledHtml.toHtmlElement().cloneNode(true);
				self.prepareElement(el); //todo: rename prepareElement
			});
		}
	},
	
	assetpath : function(filename){
		var asset_type = filename.split(".")[1];
		var url = this.asset_path.replace(/\$\{CLASSNAME\}/ig, this.getClassName()).replace(/\$\{ASSET\}/ig, asset_type).replace(/\$\{FILENAME\}/,filename);
		return url;	
	},
	
	loadasset : function(url,onloaded){
		//see xmlHttp docs at http://www.ilinsky.com/articles/XMLHttpRequest/
		var xhr = new http.XmlHttpRequest; 
		    xhr.open("GET", url, true); 
		    xhr.onreadystatechange = function() { 
		        if (this.readyState == http.XmlHttpRequest.DONE) {
					onloaded(this.responseText);
				} 
		    } 
		    setTimeout(function send_request_for_asset(){xhr.send()}, 100);
	},
	
	load_css: function(url){
		var something_went_wrong = "Error loading stylesheets. Expected an array of style urls or a single url to a stylesheet for this component.";
		var stylesheets = window.loaded_stylesheets;
		var styles = url||this.stylesheets;
		
		if (!stylesheets) {
			window.loaded_stylesheets = {};
			stylesheets = window.loaded_stylesheets;}
		if(styles) {
			if(styles instanceof Array) {
				for(var i=0; i<=styles.length-1; i++) {
					if (!stylesheets[styles[i]]) {
						this.load_css(styles[i]);
					}
				}
			}
			else if(typeof styles === "string") {
				var headID = document.getElementsByTagName("head")[0];
		        var cssNode = document.createElement('link');
		        cssNode.type = 'text/css';
		        cssNode.rel = 'stylesheet';
		        cssNode.href = styles;
		        cssNode.media = 'screen';
		        headID.appendChild(cssNode);
				stylesheets[styles] = cssNode;
				return stylesheets[styles];
			}
		}
		else {
			//styles = this.assetpath(this.getClassName() + ".css");
			//throw new Error("TODO: Load any default stylesheets for widget '" + this.getClassName() + "'.  (see: Widget.prototype.load_css()")
		}
    },
	
	set : function(key, val) { this.getModel().set(key, val); },
	
	get : function(key) { 
		var res = this.getModel().get(key); 
		if(!res || typeof res == "undefined" || res == null) {
			res = this[key];
			if(res && (typeof res !== "function")) {
				this.set(key,res);
				res = this.getModel().get(key);
			}
		}
		return res;
	},
	
    render : function(){ return this.element },
	
    getModel : function(){ return this.model; },
	
	appendChild : function(o){
		var self = this;
		this.onready(function(){
			if(Object.isElement(o)) {
				self.element.appendChild(o);
			}
			else if(o && o instanceof controls.HtmlControl) {
				if(Object.isElement(o.element)) {
					self.element.appendChild(o.element);
				}
				else {
					o.onready(function(){
						var el = o.render();
						self.element.appendChild(el);
					})
				}
			}
		});
	},
	
	add : function(o){ 
		this.appendChild(o) 
	},
	
    clone : function(_onready_callback){
		var Klass = this.constructor;
		var o = new Klass({
			html 	: (Object.isElement(this.element)) ? this.element.cloneNode(true) : null,
			model   : Object.extend({},this.getModel()),
			onready : function(){
				this.set("description", "this object is a clone");
				if(_onready_callback) _onready_callback.call(this,null)
			}
		});
		return o;
    }
	
//	indicateProgress : function(continuation){
//		console.log("loading " + this.getClassName() + "...");
//		$(this.element).appendChild("<div class='progress' style='z-index:99999;position:absolute;top:0px;left:0px;width:100%;height:100%;background-color:white;'>Loading...</div>".toHtmlElement())
////		continuation();
//	},
//	
//	removeProgress : function(el){
//		var progress = $(el || this.element).cssSelect("> .progress")[0];
//		$(progress).remove();
//	}
});
